#include "app_device.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "app_router.h"
#include <pthread.h>
#include "app_common.h"

#define DEV_WRITE_INTERVAL_MS 100

/**
 * 设备的初始化
 */
Device *app_device_init(char * filename)
{   
    Device *device = (Device *)malloc(sizeof(Device));
    //打开文件获得描述符
    device->fd = open(filename,O_CREAT|O_RDWR,0644); // 这里默认打开文件，如果文件不存在就创建文件？ 为什么要创建？
    if(device->fd < 0)
    {
        log_error("open file %s failed",filename);
        return NULL;
    }
    device->type =DEV_GENERAL;
    device->conn_type= CONN_NONE;
    device->read_buffer= app_buffer_init(1024);
    device->write_buffer= app_buffer_init(1024);
    device->is_running=0;
    device->dev_func= malloc(sizeof(DeviceFunc));
    device->last_write_ts=0;

    return device;

}


/**
 *  设备的启动
 */
int app_device_start(Device *device)
{
    //判断是否已经启动
    if(device->is_running)
    {
        log_error("device  is running" );
        return 0;
    }
    device->is_running=1;

    //启动读取线程  //为何要启动线程进行异步读取
    if(pthread_create(&device->read_thread,NULL,read_thread_func,device)<0)
    {
            log_error("create read thread failed");
            device->is_running=0;
            return -1;
    }
    return 0;

}

//该任务函数是异步的，所以需要等待
static  void *read_thread_func(void *arg)
{   char tmp_buffer[1024];
    Device *device = (Device *)arg;
    while(device->is_running) //只要启动就一直循环处理
    {  //从设备文件中读取数据
 
       int len= read(device->fd,tmp_buffer ,sizeof(tmp_buffer) );
       if(len>0)
       {
           //对内部的数据进行处理
             len= device->dev_func->read_post(device,tmp_buffer,len);
       }
       app_buffer_write(device->read_buffer,tmp_buffer,len);  //把加工处理后的数据写入内部缓存区
       //注册一个任务用于异步发送数据
       app_pool_registerTask( sendToDevFunc,device);

    }
 
    
} 


 

/**
 * 设备的停止
 */
int app_device_stop(Device *device)
{
    if(!device->is_running)
    {
        log_error("device is not running");
        return 0;
    }
    device->is_running=0;
    //结束对应的线程
    pthread_cancel(device->read_thread);
    //等待线程结束
    pthread_join(device->read_thread,NULL);
    return 0;


}


/**
 * 设备的销毁  何时需要关闭？？
 */
int app_device_close(Device *device)
{
    //关闭文件 
    close(device->fd);
    //释放buffer
    app_buffer_destroy(device->read_buffer);
    app_buffer_destroy(device->write_buffer);
    // 释放方法空间
    free(device->dev_func);
    // 释放设备空间
    free(device);


}

//该方法提交给线程池处理 不在本地处理
//在内部缓存区中读取数据，然后写入设备文件
static void  *writeToDevFunc(void *arg)
{
    Device *device = (Device *)arg;
    char data[1024];
    // 从内部缓存区读取数据
     app_buffer_read(device->write_buffer,data,3); //由于不知道应该读多少数据所以先取数据量
     app_buffer_read(device->write_buffer,data+3,data[1]+data[2]); //由于不知道应该读多少数据所以先取数据量
     int len=  3+data[1]+data[2];
 
      //对内部的数据进行处理
      len= device->dev_func->pre_write(device,data,len); 

      //保证距离上次写入留够时间
      long interval=get_current_timestamp()-device->last_write_ts;
      if(interval<DEV_WRITE_INTERVAL_MS)
      {
          usleep((interval - DEV_WRITE_INTERVAL_MS)*1000);
      }
      // 写入设备文件
     write(device->fd,data,len);
     //更新上次写入时间
     device->last_write_ts=get_current_timestamp();
     return 0;


 
}

/**
 * 负责从设备的缓存中提取数据并发送给 mqtt服务器
 */
static void *sendToDevFunc(void  *arg)
{
   Device *device=  (Device *)arg;
  //从设备缓存中提取数据 
    char data[256];
    app_buffer_read(device->read_buffer,data,3);// 不知道要从buffer中读多少数据，先从3个字节开始读获得长度
    app_buffer_read(device->read_buffer,data+3,data[1]+data[2]); 
    int len = data[1]+data[2]+3;
   //读出来的数据要转为json
    // 先转换为message 
    Message *message= app_message_initByBinary(data,len ); 
    char *json[128];
    int json_len= app_message_toJson(message,json,sizeof(json)); // 将message转换为json

    app_mqtt_sendMsg(json,json_len);

    app_message_free(message);

    return 0;

}



/**
 * 设备的接收数据
 */
int app_device_recvMsg(Device *device,char *data,int len)
{   // 先把数据写入内部缓存区
    if(app_buffer_write(device->write_buffer,data,len)<0)
    {
        log_error("write data to internal buffer failed");
        return -1;
    }

    // 注册一个任务用于异步发送数据到设备文件
    if(app_pool_registerTask( writeToDevFunc,device)<0)
    {
        log_error("register task failed");
        return -1;
    }
    return 0;
}